package com.server.gateway.handler;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;

import com.server.model.all.constant.token.TokenConstant;
import com.server.common.code.utils.GsonUtils;
import com.server.common.code.utils.ResultUtil;
import com.server.common.code.utils.UserUtils;
import com.server.common.code.vo.LoginVo;
import com.server.common.redis.token.TokenServices;
import com.server.common.redis.service.RedisBlackWhiteListService;
import com.server.common.redis.service.RedisUserBlackListService;
import com.server.gateway.utils.MessageUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Optional;

@Component
@Slf4j
public class MyFilter implements GlobalFilter, GatewayFilter, Ordered {

    @Resource
    private TokenServices tokenServices;


    @Resource
    private RedisTemplate<String, String> redisTemplate;


    @Resource
    private MessageUtils messageUtils;

    @Resource
    private RedisUserBlackListService redisUserBlackListService;


    @Resource
    private RedisBlackWhiteListService redisBlackWhiteListService;

    /**
     * 1.先获取Token数组
     * 2.判断是否有Token
     * 3.获取Token数组中第一个
     * 5.把解析后的Token有效数据放入请求头
     * @author LXC
     * @date 2022/4/25 21:58
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();

        String ipAddress = getIpAddress(exchange.getRequest());

        //黑名单IP--如果存在则404
        if(Boolean.TRUE.equals(redisBlackWhiteListService.hasBlackIpAddr(ipAddress))){
            return messageUtils.sendResponse(exchange,ResultUtil.error(404,"404 NOT FIND!"));
        }

        //白名单IP 不存在则404
        if(Boolean.TRUE.equals(redisBlackWhiteListService.hasWhiteIpAddr(ipAddress))){
            return messageUtils.sendResponse(exchange,ResultUtil.error(404,"404 NOT FIND!"));
        }

        String url = exchange.getRequest().getURI().getPath();

        if(url.contains("/api/")){
            return chain.filter(exchange);
        }

        //1.先获取Token数组
        List<String> tokenList = request.getHeaders().get(TokenConstant.TOKEN);

        //2.判断是否有Token
        if(ObjectUtils.isEmpty(tokenList)){
            return messageUtils.sendResponse(exchange,ResultUtil.error(666,"令牌不存在!"));
        }

        //3.获取Token数组中第一个
        String token = CollUtil.getFirst(tokenList);

        LoginVo loginVo = tokenServices.getToken(token);

        if(ObjectUtils.isEmpty(loginVo)){
            return messageUtils.sendResponse(exchange,ResultUtil.error(666,"令牌失效,重新登录!"));
        }

        //到期时间到
        if(loginVo.getExpireTime() <= DateUtil.current()){
            return messageUtils.sendResponse(exchange,ResultUtil.error(666,"令牌失效,重新登录!"));
        }

        //如果不是admin不可以访问feign接口
        if(url.contains("/private/") && !UserUtils.haveAdmin(loginVo.getRoleId())) {
            return messageUtils.sendResponse(exchange, ResultUtil.error("不可访问!"));
        }

        //用户-黑名单
        if(Boolean.TRUE.equals(redisUserBlackListService.hasBlackUserId(loginVo.getUserId()))){
            return messageUtils.sendResponse(exchange,ResultUtil.error(666,"404 NOT FIND!"));
        }

        //把loginVo 转换json放入请求头
        ServerHttpRequest mutableReq = exchange.getRequest().mutate()
                .header(TokenConstant.TOKEN_JSON_DATA, GsonUtils.getGson().toJson(loginVo))
                .build();

        ServerWebExchange mutableExchange = exchange.mutate().request(mutableReq).build();

        return chain.filter(mutableExchange);
    }

    @Override
    public int getOrder() {
        return -200;
    }

    @Override
    public ShortcutType shortcutType() {
        return GatewayFilter.super.shortcutType();
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return GatewayFilter.super.shortcutFieldOrder();
    }

    @Override
    public String shortcutFieldPrefix() {
        return GatewayFilter.super.shortcutFieldPrefix();
    }


    /**
     * 获取IP地址
     * @author LXC
     * @date 2023/2/2 8:24
     */
    public static String getIpAddress(ServerHttpRequest request) {
        String IP_UNKNOWN = "unknown";
        String IP_LOCAL = "127.0.0.1";
        String IPV6_LOCAL = "0:0:0:0:0:0:0:1";
        int IP_LEN = 15;

        HttpHeaders headers = request.getHeaders();
        String ipAddress = headers.getFirst("x-forwarded-for");
        if (ipAddress == null || ipAddress.length() == 0 || IP_UNKNOWN.equalsIgnoreCase(ipAddress)) {
            ipAddress = headers.getFirst("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || IP_UNKNOWN.equalsIgnoreCase(ipAddress)) {
            ipAddress = headers.getFirst("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || IP_UNKNOWN.equalsIgnoreCase(ipAddress)) {
            ipAddress = Optional.ofNullable(request.getRemoteAddress())
                    .map(address -> address.getAddress().getHostAddress())
                    .orElse("");
            if (IP_LOCAL.equals(ipAddress)|| IPV6_LOCAL.equals(ipAddress)) {
                // 根据网卡取本机配置的IP
                try {
                    InetAddress inet = InetAddress.getLocalHost();
                    ipAddress = inet.getHostAddress();
                } catch (UnknownHostException e) {
                    log.error(e.getMessage(),e);
                }
            }
        }

        // 对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
        if (ipAddress != null && ipAddress.length() > IP_LEN) {
            int index = ipAddress.indexOf(",");
            if (index > 0) {
                ipAddress = ipAddress.substring(0, index);
            }
        }
        return ipAddress;
    }
}
